#include "codegen.h"
#include "llvm/IR/Verifier.h"

/*

*/

llvm::Value *CodeGen::VisitProgram(Program *p)
{
    // 创建 printf 函数
    auto printFunctionType = llvm::FunctionType::get(irBuilder.getInt32Ty(), {irBuilder.getInt8PtrTy()}, true);
    auto printFunction = llvm::Function::Create(printFunctionType, llvm::GlobalValue::ExternalLinkage, "printf", module.get());
    // 创建 main 函数
    auto mainFunctionType = llvm::FunctionType::get(irBuilder.getInt32Ty(), false);
    auto mainFunction = llvm::Function::Create(mainFunctionType, llvm::GlobalValue::ExternalLinkage, "main", module.get());
    // 创建 main 函数的基本块
    llvm::BasicBlock *entryBlock = llvm::BasicBlock::Create(context, "entry", mainFunction);
    // 设置该基本块作为指令的入口
    irBuilder.SetInsertPoint(entryBlock);

    llvm::Value *lastVal = nullptr;
    for (auto expr : p->ExprVec)
    {
        lastVal = expr->Accept(this);
    }
    irBuilder.CreateCall(printFunction, {irBuilder.CreateGlobalStringPtr("expr value: %d\n"), lastVal});

    // 创建返回值
    llvm::Value *ret = irBuilder.CreateRet(irBuilder.getInt32(0));

    llvm::verifyFunction(*mainFunction);

    module->print(llvm::outs(), nullptr);
    return ret;
}

llvm::Value *CodeGen::VisitVariableDecl(VariableDecl *decl)
{
    llvm::Type *ty = nullptr;
    if (decl->type == CType::getIntTy())
    {
        ty = irBuilder.getInt32Ty();
    }
    llvm::StringRef text(decl->token.ptr, decl->token.length);
    llvm::Value *varAddr = irBuilder.CreateAlloca(ty, nullptr, text);
    varAddrTyMap.insert({text, {varAddr, ty}});
    return varAddr;
}

llvm::Value *CodeGen::VisitVariableAccessExpr(VariableAccessExpr *varaccExpr)
{
    llvm::StringRef text(varaccExpr->token.ptr, varaccExpr->token.length);
    std::pair pair = varAddrTyMap[text];
    llvm::Value *varAddr = pair.first;
    llvm::Type *ty = pair.second;
    if (varaccExpr->type == CType::getIntTy())
    {
        ty = irBuilder.getInt32Ty();
    }
    // 返回一个右值
    return irBuilder.CreateLoad(ty, varAddr, text);
}

// a = 3; // right value
llvm::Value *CodeGen::VisitAssignExpr(AssignExpr *assignExpr)
{
    VariableAccessExpr *varAccExpr = (VariableAccessExpr *)assignExpr->left.get();
    llvm::StringRef text(varAccExpr->token.ptr, varAccExpr->token.length);
    std::pair pair = varAddrTyMap[text];
    llvm::Value *addr = pair.first;
    llvm::Type *ty = pair.second;
    llvm::Value *rValue = assignExpr->right->Accept(this);
    // 这个得到的是一个左值
    irBuilder.CreateStore(rValue, addr);
    // 返回一个右值
    return irBuilder.CreateLoad(ty, addr,text);
}

llvm::Value *CodeGen::VisitBinaryExpr(BinaryExpr *binaryExpr)
{
    auto left = binaryExpr->left->Accept(this);
    auto right = binaryExpr->right->Accept(this);

    switch (binaryExpr->op)
    {
    case OpCode::add:
    {
        return irBuilder.CreateNSWAdd(left, right, "add"); // CreateNSW... 是防止溢出行为的
    }
    case OpCode::sub:
    {
        return irBuilder.CreateNSWSub(left, right, "sub");
    }
    case OpCode::mul:
    {
        return irBuilder.CreateNSWMul(left, right, "mul");
    }
    case OpCode::div:
    {
        return irBuilder.CreateSDiv(left, right, "div");
    }
    default:
    {
        break;
    }
    }
    return nullptr;
}

llvm::Value *CodeGen::VisitNumberExpr(NumberExpr *factorExpr)
{
    return irBuilder.getInt32(factorExpr->token.value);
}